<?php

/**
 * Description of view
 *
 * @author user
 */
class View {

    // View filename
    protected $_file;
    
    // Array of local variables
    protected $_data = array();

    /**
     * Sets the view filename and local data.
     * 
     * @param   string view filename
     * @param   array array of values
     * @return  void
     * @uses    View::set_filename
     */
    public function __construct($file = NULL, array $data = NULL) {

        if ($file !== NULL) {
            $this->set_filename($file);
        }

        if ($data !== NULL) {
            $this->_data = $data + $this->_data;
        }
    }

    /**
     * Searches for the given variable and return its value.
     * If the variable has not yet been set, error will be shown.
     *
     * $view->key
     *
     * @param   string  variable name
     * @return  mixed
     */
    public function & __get($key) {
        if (isset ($this->_data[$key])) {
            return $this->_data[$key];
        } else {
            show_error("Variabel $key could not be found in file " . $this->_file);
        }
    }

    /**
     * Calls View::set
     *
     *      $view->key = 'value';
     *
     * @param   string  variable name or an array of variables
     * @param   mixed   value
     * @return  void
     */
    public function __set($key, $value) {
        $this->set($key, $value);
    }

    /**
     * Call View::render
     *
     * @return  string
     */
    public function  __toString() {
        return $this->render();
    }

    /**
     * Returns a new View object.
     *
     * @param   string  view filename
     * @param   array   array of values
     * @return  View
     */
    public static function factory($file = NULL, array $data = NULL) {
        return new View($file, $data);
    }

    /**
     * Assigns a variable by name. Assigned values will be available as a
     * variable in the view file:
     *
     *      // This value can be accessed as $key in the view
     *      $view->set('key', 'valye');
     *
     * Can also use an array to set some values
     *
     *      // Create the values $key1 and $key2 in the view
     *      $view->set(array('key1' => 'value1', 'key2' => 'value2'));
     *
     * @param   string  variable name or an array of variables
     * @param   mixed   value
     * @return  View
     */
    public function set($key, $value = NULL) {
        if (is_array($key)) {
            foreach ($key as $name => $value) {
                $this->_data[$name] = $value;
            }
        } else {
            $this->_data[$key] = $value;
        }

        return $this;
    }

    /**
     * Sets the view filename.
     *
     * @param   string view filename
     * @return  void
     */
    public function set_filename($file) {
        $path = BASEPATH . "views/$file.php";

        if (!file_exists($path)) {
            show_error("View file $path could not be found.");
        }

        $this->_file = $path;
    }

    /**
     * Captures the outpur that is generated when view is included.
     * The view data will be extracted to make local variables.
     *
     * @param   string  filename
     * @param   array   variables
     * @return  string  HTML
     */
    protected static  function capture($filename, $data) {
        // Import the new variables to local namespace
        extract($data, EXTR_SKIP);

        // Capture the view output
        ob_start();

        try {
            // Load the view
            include $filename;
        } catch (Exception $e) {
            // Delete the output buffer
            ob_end_clean();

            // Re-throw the exception
            throw $e;
        }

        // Get the captured output and close the buffer
        return ob_get_clean();
    }

    /**
     * Renders the view object to a string.
     *
     *  $out = $view->render();
     *
     * If file is not yet set, error message will be shown.
     * 
     * @param   string  view filename
     * @return  string  
     */
    public function render($file = NULL) {
        if ($file !== NULL) {
            $this->set_filename($file);
        }

        if (empty($this->_file)) {
            show_error('View file is not yet set.');
        }

        return View::capture($this->_file, $this->_data);
    }
}

?>
